/* License Notice:
**
** This program is free software: you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation, either version 3 of the License, or
**    (at your option) any later version.
** This program is distributed in the hope that it will be useful,
**   but WITHOUT ANY WARRANTY; without even the implied warranty of
**   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
**   GNU General Public License for more details.
** You should have received a copy of the GNU General Public License
**   along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/**
 * @file text_box.cpp
 * @author TooOld2Rock'nRoll
 * @see text_box.hpp
 */
/*---- Includes ----*/
#include "graphics/text_box.hpp"

#include "toolbox/debug.h"


/*---- Global Variables ----*/


/*---- Methods Implementation ----*/
/**
 * @brief Default Constructor.
 */
TextBox::TextBox () : Sprite2D()
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    //user won't have access to this method and we need to create a texture to receive the string to render
    this->setTexture (new Texture ());
    this->getTexture()->useMonocromeAsAlpha (true);
}//End Constructor

/**
 * @brief Destructor.
 */
TextBox::~TextBox ()
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    delete (this->getTexture ());
}//End destructor


/**
 * @brief Sets a Font to be used by the TextBox.
 *
 * @param f - pointer to the Font to be used.
 */
void TextBox::setFont (Font *f)
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    if (!f)
        _debug (ERROR, "Trying to set texture to null, are your sure?");
    else if (this->p_font)
        _debug (WARNING, "This TextBox already had a Font, are you sure it should be overridden?");

    this->p_font = f;
}//End setFont ()


/**
 * @brief Sets a string to be used as text in the Label.
 *
 * @param label - the text to render.
 *
 * @throws std::runtime_error - in case there is no Font set yet.
 */
void TextBox::setString (std::string label)
{
    unsigned len = 0, i = 0,
             width = 0, height = 0,
             max_width = 0;

    const char *aux = label.c_str ();
    unsigned glyph_id = 0;

    const Font::font_stats_s *font_stats = nullptr;
    const Font::tile_uv *glyph_list = nullptr;
    const Font::glyph_stat_s *glyph_stats = nullptr;
    Texture *target = this->getTexture ();

    _debug (TRACE, "::%s (label=%s)", __FUNCTION__, aux);

    if (!this->p_font)
        throw std::runtime_error (fmt::format ("{0}:{1} - Label has no font set, can't calculate render size!", __LINE__, __FILE__));

    font_stats = &this->p_font->getStatistics ();

    glyph_list = this->p_font->getTileMap ();
    if (!glyph_list)
        throw std::runtime_error (fmt::format ("{0}:{1} - Label Font has no glyph mapped!", __LINE__, __FILE__));
    glyph_stats = this->p_font->getGlyphStatistics ();

    this->s_lable = label;
    //if string vazia, colocar caracter de espaço vazio (codepoint 0 acho....)

    //first we need to know the actual size of the string when rendered
    height = font_stats->f_line_advance; //we need to account for the first line, if any...
    len = this->s_lable.length ();
    for (i = 0; i < len; ++i)
    {
        switch (aux[i])
        {
            case '\n':
                height += font_stats->f_line_advance;
                continue;
            //end case '\n':

            case '\r':
                width = 0;
                continue;
            //end case '\r':

            default:
                try
                { width += glyph_stats[this->p_font->getCodepointList().at(aux[i])].u_advance; }
                catch (const std::out_of_range &ex) //if codepoint has no glyph, use empty/[0] as placeholder!
                { width += glyph_stats[this->p_font->getCodepointList().at(0)].u_advance; }

                if (width > max_width)
                    max_width = width;
                break;
        }//end switch (aux[len])
    }//end for (i = 0; i < len; ++i)

    //prepare the frame buffers
    target->load (nullptr, max_width, height, 1);

    Font::blitSet (this->p_font->getID (), target->getID ());

    //then we do everything again and actually render to the destination texture
    width = 0; //now this is the render position x
    height = 0; //now this is the render position y
    for (i = 0; i < len; ++i)
    {
        switch (aux[i])
        {
            case '\n':
                height += font_stats->f_line_advance;
                continue;
            //end case '\n':

            case '\r':
                width = 0;
                continue;
            //end case '\r':

            default:
                try
                { glyph_id = this->p_font->getCodepointList().at (aux[i]); }
                catch (const std::out_of_range &ex) //if codepoint has no glyph, use empty/[0] as placeholder!
                { glyph_id = this->p_font->getCodepointList().at (0); }

                Font::blit (glyph_list[glyph_id].u_x1, glyph_list[glyph_id].u_y1,
                            width + glyph_stats[glyph_id].f_xoff, height + glyph_stats[glyph_id].f_yoff,
                            glyph_list[glyph_id].u_width, glyph_list[glyph_id].u_height,
                            GL_COLOR_BUFFER_BIT, GL_NEAREST);

                width += glyph_stats[glyph_id].u_advance;
                break;
        }//end switch (aux[len])
    }//end for (i = 0; i < len; ++i)
    height += font_stats->f_line_advance; //we need to account for the first line, if any...

    Font::blitEnd ();

    this->getSize().set (max_width, height);
}//End setString ()

